package com.bac.rentmap.ad.flat.proccess;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.springframework.beans.factory.annotation.Required;

import com.bac.rentmap.ad.flat.FlatAd;
import com.bac.rentmap.ad.publisher.AdPublisherService;
import com.bac.rentmap.jdo.JdoSupport;

/**
 * Detects agents.
 * 
 * @author Andrew Osipenko
 */
public class AgentFlatAdProccessor extends JdoSupport implements
		FlatAdProccessor {
	private static final Logger log = Logger
			.getLogger(AgentFlatAdProccessor.class.getName());
	
	private static int AGENT_THRESHOLD = 3;
	
	private Pattern agentPattern = Pattern.compile("агент",
			Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);

	private Pattern notAgentPattern = Pattern.compile("не\\s+агент",
			Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);

	private int batchSize;
	private AdPublisherService adPublisherService;

	/**
	 * {@inheritDoc}
	 */
	public int proccess() {
		PersistenceManager persistenceManager = getPersistenceManager();
		try {
			Query query = buildQuery(persistenceManager);
			query.setRange(0, batchSize);
			// .setFilter("buildingDefined == true");
			@SuppressWarnings("unchecked")
			List<FlatAd> flatAds = (List<FlatAd>) query.execute();
			log.fine("Found " + flatAds.size() + " ads");
			Map<String, FlatAd> nonAgentFlatAds = new HashMap<String, FlatAd>();
			for (FlatAd flatAd : flatAds) {
			    if(flatAd.getExternal()){
			        processExternalFlatAd(flatAd);
			    }
			    else{
			        processInternalFlatAd(flatAd, nonAgentFlatAds, persistenceManager);
			    }
			}
			return flatAds.size();
		} finally {
			persistenceManager.close();
		}
	}
	
	private void processExternalFlatAd(FlatAd flatAd){
	    flatAd.setAgent(Boolean.FALSE);
	}
	
	private void processInternalFlatAd(FlatAd flatAd, Map<String, FlatAd> nonAgentFlatAds, PersistenceManager persistenceManager){
        int adCount;
        if(flatAd.getAgentCalculated() == null || !flatAd.getAgentCalculated()){
            adCount = adPublisherService.registerNewAd(flatAd
                .getPhone());
        }
        else{ // do not inc agent counter for ad updates
            adCount = adPublisherService.getAdCount(flatAd.getPhone());
        }
        boolean agent = adCount >= AGENT_THRESHOLD;
        if (!agent) {
            agent = isAgentAccordingToDescription(flatAd
                    .getDescription());
        }
        flatAd.setAgent(agent);
        if (!agent) {
            nonAgentFlatAds.put(flatAd.getPhone(), flatAd);
        }
        if (adCount == AGENT_THRESHOLD) {
            FlatAd nonAgentFlatAd = nonAgentFlatAds.get(flatAd
                    .getPhone());
            if (nonAgentFlatAd != null) {
                nonAgentFlatAd.setAgent(true);
                nonAgentFlatAds.remove(nonAgentFlatAd);
            } else {
                updateFirstAdOfAgent(persistenceManager,
                        flatAd.getPhone());
            }
        }

	}

	private Query buildQuery(PersistenceManager persistenceManager) {
		Query query = persistenceManager.newQuery(FlatAd.class);
		query.setFilter("agentCalculated == false");
		return query;
	}

	private void updateFirstAdOfAgent(PersistenceManager persistenceManager,
			String phone) {
		Query query = persistenceManager.newQuery(FlatAd.class);
		query.setFilter("agent == false && phone == '" + phone + "'");
		query.setUnique(true);
		FlatAd flatAd = (FlatAd) query.execute();
		if (flatAd != null) {
			log.info("Reseting rating for " + flatAd
					+ " because it was published by agent");
			flatAd.setAgent(true);
		} else {
			log.finest("First " + phone + " agent flat was removed");
		}
	}

	boolean isAgentAccordingToDescription(String description) {
	    if(description == null){
	        return false;
	    }
		Matcher matcher = agentPattern.matcher(description);
		if (!matcher.find()) {
			return false;
		}
		matcher = notAgentPattern.matcher(description);
		return !matcher.find();
	}

	@Required
	public void setBatchSize(int batchSize) {
		this.batchSize = batchSize;
	}

	@Required
	public void setAdPublisherService(AdPublisherService adPublisherService) {
		this.adPublisherService = adPublisherService;
	}

	public int getQueueSize() {
		PersistenceManager persistenceManager = getPersistenceManager();
		try {
			Query query = buildQuery(persistenceManager);
			query.setResult("count(this)");
			Object res = query.execute();
			if(res == null)
			    return 0;
			return ((Number) res).intValue();
		} finally {
			persistenceManager.close();
		}
	}

	public int getBatchSize() {
		return batchSize;
	}

}
